/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/graphics/dx11.1/details/common.hpp>
#include <baja/graphics/dx11.1/graphics_fwd.hpp>
#include <baja/graphics/dx11.1/details/enum_mappings.hpp>
#include <baja/graphics/dx11.1/graphics_classes.hpp>
#include <baja/graphics/dx11.1/details/util.hpp>
#include <baja/stream/stream.hpp>

namespace baja { namespace graphics { namespace dx11_1 {

namespace d = baja::graphics::dx11_1::details;

std::shared_ptr<isurface> createSurfaceFromView(ID3D11View* view, const texture_type type);

inline engine::engine(const engine::create_params& p)
{
    BAJA_VERIFY(p.dxgiSwapChain != nullptr);
    BAJA_VERIFY(p.d3d11Device != nullptr);
    BAJA_VERIFY(p.d3d11DeviceContext != nullptr);

    _dxgiSwapChain = p.dxgiSwapChain;
    _d3d11Device = p.d3d11Device;
    _d3d11DeviceContext = p.d3d11DeviceContext;
    _d3d11DeviceContextDeferred = p.d3d11DeviceContextDeferred;
    _d3d11FeatureLevel = p.d3d11FeatureLevel;

    _displayParams = p.displayParams;

    _engineName = p.engineName;

    _currentRenderMode = g::render_mode::normal;
    _nextRenderMode = g::render_mode::normal;

    _msaaCount = p.msaaCount;
    _msaaQualityLevel = p.msaaQualityLevel;

    _resetPeakStats = false;

    _time = 0.0f;

    // Y and Z need to flip so graphics pipeline 
    _coordinateSystemMatrix.makeIdentity();
    _coordinateSystemMatrix.m[1][1] = 0.0f;
    _coordinateSystemMatrix.m[1][2] = 1.0f;

    _coordinateSystemMatrix.m[2][1] = 1.0f;
    _coordinateSystemMatrix.m[2][2] = 0.0f;

    // init default state descriptions
    _defaultBlendDesc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
    _defaultDepthStencilDesc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT());
    _defaultRasterizerDesc = CD3D11_RASTERIZER_DESC(CD3D11_DEFAULT());

    _isWarp = p.isWarp;

    _d2d1Factory = p.d2d1Factory;
    _d2d1Device = p.d2d1Device;
    _d2d1DeviceContext = p.d2d1DeviceContext;
}

inline engine::~engine()
{
    uint32 i = 0;

    // take graphics out of the var console
    std::shared_ptr<ivar_group> parent;
    if (_graphicsVarGroup)
    {
        parent = _graphicsVarGroup->getParent();
        if (parent)
        {
            parent->removeChildGroup(_graphicsVarGroup);
        }
        _graphicsVarGroup = nullptr;
    }

    if (_assetManager)
    {
        //_assetManager->releaseAllAssets();
        _assetManager = nullptr;
    }

    _overdrawShader.Reset();
    _wireframeShader.Reset();
    _nullTexture = nullptr;
    _material = nullptr;

    for (i = 0; i < g::constants::maxTextures; i++)
    {
        _textures[i] = nullptr;
    }

    for (i = 0; i < static_cast<uint32>(standard_effect::total); i++)
    {
        _standardEffects[i] = nullptr;
    }

    _materialBuffer.Reset();
    _lightBuffer.Reset();
    _objectBuffer.Reset();
    _miscBuffer.Reset();

    _blendStateMap.clear();
    _depthStencilStateMap.clear();
    _rasterizerStateMap.clear();

    _worldIMBuffer = nullptr;
    _screenIMBuffer = nullptr;

    // delete lights array
    _lights.clear();

    // release the NULL texture
    _nullTexture = nullptr;

    // release the d3d11 objects
    _d3d11DeviceContext->ClearState();
    _d3d11DeviceContext->Flush();
    _d3d11DeviceContextDeferred->ClearState();
    _d3d11DeviceContextDeferred->Flush();
    _dxgiSwapChain.Reset();

    _d3d11Device.Reset();
    _d3d11DeviceContext.Reset();

    //_d2d1DeviceContext->Clear();
    //_d2d1DeviceContext->Flush();

    _d2d1StateBlock.Reset();
    _d2d1Device.Reset();
    _d2d1DeviceContext.Reset();
    _d2d1Factory.Reset();
}

inline std::shared_ptr<engine> engine::create(
    const std::shared_ptr<iservice_map>& services,
    const bool useWARP,
    const display_params& displayParams,
    const void* context
    )
{
    win32_result wr;

    BAJA_CHECK_ARG(services != nullptr);
    BAJA_CHECK_ARG(context != nullptr);

    // create the device
    WRL::ComPtr<ID3D11Device> d3d11Device;
    WRL::ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
    D3D_FEATURE_LEVEL supportedFeatureLevels[] = 
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };

    UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#if defined(_DEBUG)
    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_FEATURE_LEVEL d3d11FeatureLevel = D3D_FEATURE_LEVEL_11_0;
    wr = D3D11CreateDevice(
            NULL,
            useWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_HARDWARE,
            NULL,
            creationFlags,
            supportedFeatureLevels,
            sizeof(supportedFeatureLevels)/sizeof(D3D_FEATURE_LEVEL),
            D3D11_SDK_VERSION,
            &d3d11Device,
            &d3d11FeatureLevel,
            &d3d11DeviceContext);

    // TRACK: we can avoid creating a deferred context until it's needed
    // create deferred context 
    WRL::ComPtr<ID3D11DeviceContext> d3d11DeviceContextDeferred;
    wr = d3d11Device->CreateDeferredContext(0, &d3d11DeviceContextDeferred);

    // find MSAA parameters that will work for the device
    uint32 msaaCount = (displayParams.useFSAA ? 4 : 1);
    uint32 msaaQualityLevel = 0;

    while (msaaCount > 1)
    {
        uint32 numQualityLevels;

        wr = d3d11Device->CheckMultisampleQualityLevels(
            d::toDXGI_FORMAT(displayParams.backBufferFormat),
            msaaCount,
            &numQualityLevels
            );

        // if this MSAA has valid quality levels, we can break out of loop
        if (numQualityLevels > 0)
        {
            msaaQualityLevel = numQualityLevels - 1;
            break;
        }

        // drop down the MSAA count and try again
        msaaCount--;
    }

    // get the DXGI device from the D3D11 device
    WRL::ComPtr<IDXGIDevice1> dxgiDevice;
    wr = d3d11Device.As(&dxgiDevice);

    // Ensure that DXGI does not queue more than one frame at a time. This both reduces 
    // latency and ensures that the application will only render after each VSync, minimizing 
    // power consumption.
    wr = dxgiDevice->SetMaximumFrameLatency(1);

    // get the DXGI adapter from the DXGI device
    WRL::ComPtr<IDXGIAdapter1> dxgiAdapter;
    wr = dxgiDevice->GetParent(__uuidof(IDXGIAdapter1), (void**)&dxgiAdapter);

    // get the DXGI factory from the adapter
    WRL::ComPtr<IDXGIFactory2> dxgiFactory; // NOTE: used to be IDXGIFactory1
    wr = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), (void**)&dxgiFactory);

    // initialize swap chain description
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
    swapChainDesc.Width = displayParams.backBufferWidth;
    swapChainDesc.Height = displayParams.backBufferHeight;
    swapChainDesc.Format = d::toDXGI_FORMAT(displayParams.backBufferFormat);
    swapChainDesc.SampleDesc.Count = msaaCount;
    swapChainDesc.SampleDesc.Quality = msaaQualityLevel;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    swapChainDesc.Flags = 0;

    // create a swap chain for the device
    context_basic ctx = static_cast<context_basic>(context);
    WRL::ComPtr<IDXGISwapChain1> dxgiSwapChain;
    if (ctx.shellType == shell_type::basic)
    {
        wr = dxgiFactory->CreateSwapChainForCoreWindow(
        d3d11Device.Get(),
        ctx.window,
        &swapChainDesc,
        nullptr,
        &dxgiSwapChain
        );
    }
    else if (ctx.shellType == shell_type::directUI)
    {
        context_directui ctx2 = static_cast<context_directui>(context);

        wr = dxgiFactory->CreateSwapChainForComposition(
            d3d11Device.Get(),
            &swapChainDesc,
            nullptr,
            &dxgiSwapChain
            );

        wr = ctx2.panelNative->SetSwapChain(dxgiSwapChain.Get());
    }
    else
    {
        BAJA_THROW(std::logic_error("invalid context"));
    }

    // build up the engine name, appending feature level onto it
    std::wstring engineName = (useWARP ? globals::warpEngineDisplayName : globals::engineDisplayName);
    engineName.append(globals::featureLevelAddendum);
    if (d3d11FeatureLevel == D3D_FEATURE_LEVEL_9_1)
    {
        engineName.append(L"9.1");
    }
    else if (d3d11FeatureLevel == D3D_FEATURE_LEVEL_9_2)
    {
        engineName.append(L"9.2");
    }
    else if (d3d11FeatureLevel == D3D_FEATURE_LEVEL_9_3)
    {
        engineName.append(L"9.3");
    }
    else if (d3d11FeatureLevel == D3D_FEATURE_LEVEL_10_0)
    {
        engineName.append(L"10.0");
    }
    else if (d3d11FeatureLevel == D3D_FEATURE_LEVEL_10_1)
    {
        engineName.append(L"10.1");
    }
    else if (d3d11FeatureLevel == D3D_FEATURE_LEVEL_11_0)
    {
        engineName.append(L"11.0");
    }

    // Get the 11.1 interfaces
    WRL::ComPtr<ID3D11Device1> d3d11Device1;
    wr = d3d11Device.As(&d3d11Device1);

    WRL::ComPtr<ID3D11DeviceContext1> d3d11DeviceContext1;
    wr = d3d11DeviceContext.As(&d3d11DeviceContext1);

    WRL::ComPtr<ID3D11DeviceContext1> d3d11DeviceContextDeferred1;
    wr = d3d11DeviceContextDeferred.As(&d3d11DeviceContextDeferred1);

    // initialize direct 2d	
    WRL::ComPtr<ID2D1Factory1> d2d1Factory;
    WRL::ComPtr<ID2D1Device> d2d1Device;
    WRL::ComPtr<ID2D1DeviceContext> d2d1DeviceContext;

    D2D1_FACTORY_OPTIONS options;
    ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));

#if defined(BAJA_DEBUG)
     // If the project is in a debug build, enable Direct2D debugging via SDK Layers
    options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif

    wr = D2D1CreateFactory(
            D2D1_FACTORY_TYPE_SINGLE_THREADED, 
            __uuidof(ID2D1Factory1),
            &options, 
            &d2d1Factory
            );

    // Create d2d device and device context
    wr = d2d1Factory->CreateDevice(dxgiDevice.Get(), d2d1Device.GetAddressOf());
    wr = d2d1Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2d1DeviceContext.GetAddressOf());	

    // create the engine object
    engine::create_params p(
        dxgiSwapChain.Get(),
        d3d11Device1.Get(),
        d3d11DeviceContext1.Get(),
        d3d11DeviceContextDeferred1.Get(),
        d3d11FeatureLevel,
        engineName,
        displayParams,
        msaaCount,
        msaaQualityLevel,
        useWARP,
        d2d1Device.Get(),
        d2d1DeviceContext.Get(),
        d2d1Factory.Get()
        );

    std::shared_ptr<engine> e = std::make_shared<engine>(p);

    e->initialize(services);

    return e;
}

inline display_params engine::getDisplayParams()
{
    return _displayParams;
}

inline void engine::changeDisplayParams(const display_params& displayParams)
{
    // remember new display params
    _displayParams = displayParams;

    _d3d11DeviceContext->ClearState();
    _d2d1DeviceContext->SetTarget(NULL);

    win32_result wr;

    wr = _dxgiSwapChain->ResizeBuffers(
        2, 
        displayParams.backBufferWidth, 
        displayParams.backBufferHeight,
        d::toDXGI_FORMAT(displayParams.backBufferFormat),
        DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
        );

    // reset the render targets
    this->setRenderTargetViews();
    this->setD2DRenderTarget();

    this->initMembers(true);
    this->forceGraphicsIntoKnownState();

    // Get size from back buffer (in case display params height and width is zero, which means, full screen)
    DXGI_SWAP_CHAIN_DESC desc = {0};
    _dxgiSwapChain->GetDesc(&desc);

    // set viewport (which also causes viewport constant buffer to update)
    viewport vp;
    vp.position.x = 0.0f;
    vp.position.y = 0.0f;
    vp.width = (float32)desc.BufferDesc.Width;
    vp.height = (float32)desc.BufferDesc.Height;
    vp.minZ = 0.0f;
    vp.maxZ = 1.0f;
    this->setViewport(vp); // NOTE: this used to be cast to SAFEHR
}

inline bool engine::getMultisampleLevels(const pixel_format format, uint32* msaaCount, uint32* msaaQualityLevel)
{
    *msaaCount = 4;
    *msaaQualityLevel = 0;

    uint32 numQualityLevels;

    win32_result wr;

    while (*msaaCount > 1)
    {
        wr = _d3d11Device->CheckMultisampleQualityLevels(
            d::toDXGI_FORMAT(format),
            *msaaCount,
            &numQualityLevels
            );

        // if this MSAA has valid quality levels, we can break out of loop
        if (numQualityLevels > 0)
        {
            *msaaQualityLevel = numQualityLevels - 1;
            break;
        }

        // drop down the MSAA count and try again
        msaaCount--;
    }

    return (*msaaCount == 0) ? false : true;
}

// d3d_wrapper methods

// get count of interfaces we offer
inline size_t engine::getInterfaceCount()
{
    return 8;
}

// gets specific interface
inline WRL::ComPtr<IUnknown> engine::getInterface(size_t index)
{
    WRL::ComPtr<IUnknown> p;
    if (index == 0)
    {
        win32_result((HRESULT)_d3d11DeviceContext.As(&p));
    }
    else if (index == 1)
    {
        win32_result((HRESULT)_dxgiSwapChain.As(&p));
    }
    else if (index == 2)
    {
        win32_result((HRESULT)_d2d1DeviceContext.As(&p));
    }
    else if (index == 3)
    {
        win32_result((HRESULT)_d2d1Factory.As(&p));
    }
    else if (index == 4)
    {
        win32_result((HRESULT)_d2d1StateBlock.As(&p));
    }
    else if (index == 6)
    {
        win32_result((HRESULT)_d3d11Device.As(&p));
    }
    else if (index == 7)
    {
        win32_result((HRESULT)_d3d11DeviceContextDeferred.As(&p));
    }
    return p;
}

// Public engine methods

// gets current blend state
inline void engine::getCurrentBlendState(
    D3D11_BLEND_DESC* blendDesc, 
    float32* blendFactor, 
    uint32* sampleMask,
    const uint32 context
    )
{
    if (blendDesc != NULL && blendFactor != NULL && sampleMask != NULL)
    {
        // get current blend state
        WRL::ComPtr<ID3D11BlendState> state;
        this->getContext(context)->OMGetBlendState(state.GetAddressOf(), blendFactor, sampleMask);
        if (state != NULL)
        {
            state->GetDesc(blendDesc);
        }
        else
        {
            *blendDesc = _defaultBlendDesc;
        }
    }
}

// sets blend state only if actually different
inline void engine::setBlendState(
    const D3D11_BLEND_DESC& blendDesc, 
    const float32* blendFactor, 
    uint32 sampleMask,
    const uint32 context
    )
{
    D3D11_BLEND_DESC stateDesc = blendDesc;

    // adjust state due to current render mode
    if (_currentRenderMode == render_mode::wireframe)
    {
        stateDesc.RenderTarget[0].BlendEnable = FALSE;
    }
    else if (_currentRenderMode == render_mode::overdraw)
    {
        stateDesc.RenderTarget[0].BlendEnable = TRUE;
        stateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
        stateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
        stateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
        stateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
        stateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
        stateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
    }

    // get current state
    D3D11_BLEND_DESC currentDesc;
    float32 currentBlendFactor[4];
    uint32 currentSampleMask;
    this->getCurrentBlendState(&currentDesc, currentBlendFactor, &currentSampleMask, context);

    // compare object's descriptions
    const color* c1 = (const color*)currentBlendFactor;
    const color* c2 = (const color*)blendFactor;
    bool setState = ((currentSampleMask != sampleMask) || (*c1 != *c2) ||
                     (std::memcmp(&currentDesc, &stateDesc, sizeof(D3D11_BLEND_DESC)) != 0));

    // set state if needed
    if (setState)
    {
        // check state map first for the state object
        WRL::ComPtr<ID3D11BlendState> state = _blendStateMap[stateDesc];
        if (state == NULL)
        {
            // state object doesn't exist, so create it
            _d3d11Device->CreateBlendState(&stateDesc, &state);
            _blendStateMap[stateDesc] = state;
        }

        // if valid, set the new state
        if (state != NULL)
        {
            this->getContext(context)->OMSetBlendState(state.Get(), blendFactor, sampleMask);
        }
    }
}

// gets current depth stencil state
inline void engine::getCurrentDepthStencilState(
    D3D11_DEPTH_STENCIL_DESC* depthStencilDesc, 
    uint32* stencilRef,
    const uint32 context
    )
{
    if (depthStencilDesc != NULL && stencilRef != NULL)
    {
        // get current depth stencil state
        WRL::ComPtr<ID3D11DepthStencilState> state;
        this->getContext(context)->OMGetDepthStencilState(state.GetAddressOf(), stencilRef);
        if (state != NULL)
        {
            state->GetDesc(depthStencilDesc);
        }
        else
        {
            *depthStencilDesc = _defaultDepthStencilDesc;
        }
    }
}

// sets depth stencil state only if actually different
inline void engine::setDepthStencilState( 
    const D3D11_DEPTH_STENCIL_DESC& depthStencilDesc,
    uint32 stencilRef,
    const uint32 context
    )
{
    D3D11_DEPTH_STENCIL_DESC stateDesc = depthStencilDesc;

    // get current state
    D3D11_DEPTH_STENCIL_DESC currentDesc;
    uint32 currentStencilRef;
    this->getCurrentDepthStencilState(&currentDesc, &currentStencilRef, context);

    // compare object's descriptions
    bool setState = ((currentStencilRef != stencilRef) ||  
                     (memcmp(&currentDesc, &stateDesc, sizeof(D3D11_DEPTH_STENCIL_DESC)) != 0));

    // set state if needed
    if (setState)
    {
        // check state map first for the state object
        WRL::ComPtr<ID3D11DepthStencilState> state = _depthStencilStateMap[stateDesc];
        if (state == NULL)
        {
            // state object doesn't exist, so create it
            _d3d11Device->CreateDepthStencilState(&stateDesc, &state);
            _depthStencilStateMap[stateDesc] = state;
        }

        // if valid, set the new state
        if (state != NULL)
        {
            this->getContext(context)->OMSetDepthStencilState(state.Get(), stencilRef);
        }
    }
}

// gets current rasterizer state
inline void engine::getCurrentRasterizerState(
    D3D11_RASTERIZER_DESC* rasterizerDesc,
    const uint32 context
    )
{
    if (rasterizerDesc != NULL)
    {
        // get current rasterizer state
        WRL::ComPtr<ID3D11RasterizerState> state;
        this->getContext(context)->RSGetState(&state);
        if (state != NULL)
        {
            state->GetDesc(rasterizerDesc);
        }
        else
        {
            *rasterizerDesc = _defaultRasterizerDesc;
        }
    }
}

// sets rasterizer state only if actually different
inline void engine::setRasterizerState( 
    const D3D11_RASTERIZER_DESC& rasterizerDesc,
    const uint32 context
    )
{
    D3D11_RASTERIZER_DESC stateDesc = rasterizerDesc;

    // always set MSAA flag to expected value
    if (_displayParams.useFSAA)
    {
        stateDesc.MultisampleEnable = TRUE;
        stateDesc.AntialiasedLineEnable = FALSE;
    }
    else
    {
        stateDesc.MultisampleEnable = FALSE;
        stateDesc.AntialiasedLineEnable = FALSE;
    }

    // adjust state due to current render mode
    if (_currentRenderMode == render_mode::wireframe)
    {
        stateDesc.FillMode = D3D11_FILL_WIREFRAME;
        stateDesc.CullMode = D3D11_CULL_NONE;
    }

    // get current state
    D3D11_RASTERIZER_DESC currentDesc;
    this->getCurrentRasterizerState(&currentDesc, context);

    // compare object's descriptions
    bool setState = (memcmp(&currentDesc, &stateDesc, sizeof(D3D11_RASTERIZER_DESC)) != 0);

    // set state if needed
    if (setState)
    {
        // check state map first for the state object
        WRL::ComPtr<ID3D11RasterizerState> state = _rasterizerStateMap[stateDesc];
        if (state == NULL)
        {
            // state object doesn't exist, so create it
            _d3d11Device->CreateRasterizerState(&stateDesc, &state);
            _rasterizerStateMap[stateDesc] = state;
        }

        // if valid, set the new state
        if (state != NULL)
        {
            this->getContext(context)->RSSetState(state.Get());
        }
    }
}

// sets the specified render state
inline void engine::setVertexDeclaration(const std::shared_ptr<ivertex_declaration>& vertexDecl, const uint32 context)
{
    WRL::ComPtr<ID3D11InputLayout> d3dInputLayout;
    
    d3dInputLayout = b::bajaInterfaceToCom<ivertex_declaration, ID3D11InputLayout>(vertexDecl);
    this->getContext(context)->IASetInputLayout(d3dInputLayout.Get());
}

inline bool engine::getIsHardwareAccelerated()
{
    return !_isWarp;
}

inline void engine::setAlphaBlend(const enabled_state state, const uint32 context)
{
    // get current blending state
    D3D11_BLEND_DESC blendDesc;
    float32 blendFactor[4];
    uint32 sampleMask;
    this->getCurrentBlendState(&blendDesc, blendFactor, &sampleMask, context);
    
    // create a new blending state, based off the old values
    blendDesc.RenderTarget[0].BlendEnable = d::toBOOL(state);

    // set new blending state
    this->setBlendState(blendDesc, blendFactor, sampleMask, context);
}

inline void engine::setAmbientLight(const color& color)
{
    // compare colors but ignore alpha
    if (_ambientLight.r != color.r ||
        _ambientLight.g != color.g ||
        _ambientLight.b != color.b)
    {
        _ambientLight = color;
        _ambientLight.a = 0.0f;

        this->updateLightConstantBuffer();
    }
}

inline void engine::setBlendOp(const blend_op_state& state, const uint32 context)
{
    // get current blending state
    D3D11_BLEND_DESC blendDesc;
    float32 blendFactor[4];
    uint32 sampleMask;
    this->getCurrentBlendState(&blendDesc, blendFactor, &sampleMask, context);
    
    // create a new blending state, based off the old values
    blendDesc.RenderTarget[0].BlendOp = d::toD3D11_BLEND_OP(state);
    blendDesc.RenderTarget[0].BlendOpAlpha = d::toD3D11_BLEND_OP(state);

    // set new blending state
    this->setBlendState(blendDesc, blendFactor, sampleMask, context);
}

inline void engine::setCulling(const cull_state state, const uint32 context)
{
    // get current rasterizer state
    D3D11_RASTERIZER_DESC rasterizerDesc;
    this->getCurrentRasterizerState(&rasterizerDesc, context);

    // create a new rasterizer state, based off the old values
    if (state == cull_state::counterClockwise)
    {
        rasterizerDesc.CullMode = D3D11_CULL_BACK;
        rasterizerDesc.FrontCounterClockwise = FALSE;
    }
    else if (state == cull_state::none)
    {
        rasterizerDesc.CullMode = D3D11_CULL_NONE;
    }
    else if (state == cull_state::clockwise)
    {
        rasterizerDesc.CullMode = D3D11_CULL_BACK;
        rasterizerDesc.FrontCounterClockwise = TRUE;
    }

    // set new rasterizer state
    this->setRasterizerState(rasterizerDesc, context);
}

inline void engine::setDstBlend(const blend_state state, const uint32 context)
{
    // get current blending state
    D3D11_BLEND_DESC blendDesc;
    float32 blendFactor[4];
    uint32 sampleMask;
    this->getCurrentBlendState(&blendDesc, blendFactor, &sampleMask, context);
    
    // create a new blending state, based off the old values
    blendDesc.RenderTarget[0].DestBlend = d::toD3D11_BLEND(state);
    blendDesc.RenderTarget[0].DestBlendAlpha = d::toD3D11_BLEND(state);

    // set new blending state
    this->setBlendState(blendDesc, blendFactor, sampleMask, context);
}

inline void engine::setFillMode(const fill_mode state, const uint32 context)
{
    // get current rasterizer state
    D3D11_RASTERIZER_DESC rasterizerDesc;
    this->getCurrentRasterizerState(&rasterizerDesc, context);

    // create a new rasterizer state, based off the old values
    if (state == fill_mode::solid)
    {
        rasterizerDesc.FillMode = D3D11_FILL_SOLID;
    }
    else if (state == fill_mode::wireframe)
    {
        rasterizerDesc.FillMode = D3D11_FILL_WIREFRAME;
    }

    // set new rasterizer state
    this->setRasterizerState(rasterizerDesc, context);
}

inline void engine::setIndices(const std::shared_ptr<iindex_buffer>& indexBuffer, const uint32 context)
{
    WRL::ComPtr<ID3D11Buffer> d3dIndexBuffer;
    
    d3dIndexBuffer = b::bajaInterfaceToCom<iindex_buffer, ID3D11Buffer>(indexBuffer);
    this->getContext(context)->IASetIndexBuffer(
        d3dIndexBuffer.Get(), 
        DXGI_FORMAT_R16_UINT, 
        0);
}

inline void engine::setLight(const uint32 index, const light& l)
{
    BAJA_CHECK_ARG(index < g::constants::maxLights);

    if (memcmp(&(_lights[index].light), &l, sizeof(light)) != 0)
    {
        _lights[index].light = l;
        this->updateLightConstantBuffer();
    }
}

inline void engine::setLightEnable(const uint32 index, const enabled_state state)
{
    BAJA_CHECK_ARG(index < g::constants::maxLights);

    bool enabled = d::toBOOL(state);

    if (_lights[index].enabled != enabled)
    {
        _lights[index].enabled = enabled;
        this->updateLightConstantBuffer();
    }
}

inline void engine::setMaterial(const std::shared_ptr<imaterial>& material)
{
   bool updateMaterialConstantBuffer = false;

   // either material NULL?
   if ((_material.get() == nullptr|| material.get() == nullptr))
   {
        // create a default material and force update of constant buffer
        _material = createMaterial();
        updateMaterialConstantBuffer = true;
   }

    // check for equality
    if (material.get() != nullptr)
    {
        bool areEqual = _material->equals(material);
        updateMaterialConstantBuffer = (updateMaterialConstantBuffer || !areEqual);

        // assign new material
        _material = material;
    }

    // if material is dirty, we must update
    if (!updateMaterialConstantBuffer)
    {
        updateMaterialConstantBuffer = _material->getIsDirty();
    }

    if (updateMaterialConstantBuffer)
    {
        this->updateMaterialConstantBuffer();

        // make sure to reset dirty flag
        _material->resetIsDirty();
    }

    // update material textures
    if (_material.get() != nullptr)
    {
        for (uint32 i = 0; i < g::constants::maxTextures; i++)
        {
            std::shared_ptr<itexture> tex;
            tex = _material->getTextureAtIndex(i);
            this->setTexture(i, tex);
        }
    }
}

inline void engine::setSrcBlend(const blend_state state, const uint32 context)
{
    // get current blending state
    D3D11_BLEND_DESC blendDesc;
    float32 blendFactor[4];
    uint32 sampleMask;
    this->getCurrentBlendState(&blendDesc, blendFactor, &sampleMask, context);
    
    // create a new blending state, based off the old values
    blendDesc.RenderTarget[0].SrcBlend = d::toD3D11_BLEND(state);
    blendDesc.RenderTarget[0].SrcBlendAlpha = d::toD3D11_BLEND(state);

    // set new blending state
    this->setBlendState(blendDesc, blendFactor, sampleMask, context);
}

inline void engine::setStencil(const enabled_state state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.StencilEnable = d::toBOOL(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilFail(const stencil_op state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.FrontFace.StencilFailOp = d::toD3D11_STENCIL_OP(state);
    depthStencilDesc.BackFace.StencilFailOp = d::toD3D11_STENCIL_OP(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilZFail(const stencil_op state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.FrontFace.StencilDepthFailOp = d::toD3D11_STENCIL_OP(state);
    depthStencilDesc.BackFace.StencilDepthFailOp = d::toD3D11_STENCIL_OP(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilPass(const stencil_op state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.FrontFace.StencilPassOp = d::toD3D11_STENCIL_OP(state);
    depthStencilDesc.BackFace.StencilPassOp = d::toD3D11_STENCIL_OP(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilCompare(const compare_state state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.FrontFace.StencilFunc = d::toD3D11_COMPARISON_FUNC(state);
    depthStencilDesc.BackFace.StencilFunc = d::toD3D11_COMPARISON_FUNC(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilRefValue(const uint32 refValue, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    stencilRef = refValue;

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilCompareMaskValue(const uint32 maskValue, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.StencilReadMask = (uint8)maskValue;

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

inline void engine::setStencilWriteMaskValue(const uint32 maskValue, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.StencilWriteMask = (uint8)maskValue;

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

// sets the specified render state
inline void engine::setStreamSource(
    const uint32 streamIndex,
    std::shared_ptr<ivertex_buffer>& streamData,
    const uint32 offsetInBytes,
    const uint32 vertexStride,
    const uint32 context
    )
{
    if (!streamData)
    {
        ID3D11Buffer* p = NULL;
        UINT ui = 0;
        this->getContext(context)->IASetVertexBuffers(
        0,
        1,
        &p,
        &ui,
        &ui);
    }
    else
    {
        WRL::ComPtr<ID3D11Buffer> d3dVertexBuffer;  
        d3dVertexBuffer = b::bajaInterfaceToCom<ivertex_buffer, ID3D11Buffer>(streamData);
        ID3D11Buffer* d3dBufferArray[1];
        d3dBufferArray[0] = d3dVertexBuffer.Get();

        this->getContext(context)->IASetVertexBuffers(
            streamIndex,
            1,
            d3dBufferArray,
            &vertexStride,
            &offsetInBytes);
    }
}

inline void engine::setTexture(
    const uint32 textureIndex,
    const std::shared_ptr<itexture>& texture
    )
{
    BAJA_CHECK_ARG(textureIndex < g::constants::maxTextures);

    std::shared_ptr<itexture> newTexture = (texture.get() != nullptr? texture : _nullTexture);
    _textures[textureIndex] = newTexture;
}

inline void engine::setTime(const float32 time)
{
    _time = time;
    this->updateMiscConstantBuffer();
}

inline void engine::getViewport(viewport* vp, const uint32 context)
{
    BAJA_CHECK_ARG(vp);

    uint32 numViewports = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
    D3D11_VIEWPORT d3dViewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];

    this->getContext(context)->RSGetViewports(&numViewports, d3dViewports);
    *vp = d::toBajaViewport(d3dViewports[0]);
}

inline void engine::setViewport(const viewport& vp, const uint32 context)
{
    D3D11_VIEWPORT d3dViewport;

    d3dViewport = d::toD3D11_VIEWPORT(vp);
    this->getContext(context)->RSSetViewports(1, &d3dViewport);
    
    this->updateMiscConstantBuffer(context);
}

// sets the specified render state
inline void engine::setZBias(const int32 bias, const uint32 context)
{
    // get current rasterizer state
    D3D11_RASTERIZER_DESC rasterizerDesc;
    this->getCurrentRasterizerState(&rasterizerDesc, context);

    // create a new rasterizer state, based off the old values
    rasterizerDesc.DepthBias = bias;
    rasterizerDesc.DepthBiasClamp = 0.0f;
    rasterizerDesc.SlopeScaledDepthBias = 0.0f;

    // set new rasterizer state
    this->setRasterizerState(rasterizerDesc, context);
}

// sets the specified render state
inline void engine::setZBuffer(const enabled_state state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.DepthEnable = d::toBOOL(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

// sets the specified render state
inline void engine::setZWrite(const enabled_state state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.DepthWriteMask = d::toD3D11_DEPTH_WRITE_MASK(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

// sets the specified render state
inline void engine::setZCompare(const compare_state state, const uint32 context)
{
    // get current depth stencil state
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    uint32 stencilRef;
    this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef, context);
    
    // create a new depth stencil state, based off the old values
    depthStencilDesc.DepthFunc = d::toD3D11_COMPARISON_FUNC(state);

    // set new depth stencil state
    this->setDepthStencilState(depthStencilDesc, stencilRef, context);
}

// generate mips for specified texture
inline void engine::generateMips(const std::shared_ptr<itexture>& texture)
{
    // get shader resource view from the texture
    WRL::ComPtr<ID3D11ShaderResourceView> shaderResource;
    std::shared_ptr<b::iunknown_provider> w = std::dynamic_pointer_cast<b::iunknown_provider>(texture);
    WRL::ComPtr<IUnknown> punk = w->getInterface(d::texture_interfaces::shader_resource);
    punk.As(&shaderResource);

    _d3d11DeviceContext->GenerateMips(shaderResource.Get());
}

inline std::shared_ptr<iasset_manager> engine::getAssetManager()
{
    return _assetManager;
}

// clears the buffers
inline void engine::clear(
    const clear_targets clearTargets,
    const packed_color packedColor,
    const float32 zValue,
    const uint32 stencilValue
    )
{
    WRL::ComPtr<ID3D11RenderTargetView> renderTargetView;
    WRL::ComPtr<ID3D11DepthStencilView> depthStencilView;

    _d3d11DeviceContext->OMGetRenderTargets(
        1,
        &renderTargetView,
        &depthStencilView
        );

    color clearColor(packedColor);
    
    if (renderTargetView.Get() != NULL)
    {
        _d3d11DeviceContext->ClearRenderTargetView(
            renderTargetView.Get(), 
            (float32*)&clearColor
            );
    }

    uint32 depthClearFlags = d::toD3D11_CLEAR_FLAG(clearTargets);
    if (depthStencilView.Get() != NULL && depthClearFlags != 0)
    {
        _d3d11DeviceContext->ClearDepthStencilView(
            depthStencilView.Get(), 
            depthClearFlags,
            zValue,
            (uint8)stencilValue
            );
    }
}

// Gets snapshot of current scene transformations, setting them inside the specified iscene_transformer
inline std::shared_ptr<iscene_transformer> engine::snapshotCurrentSceneTransforms()
{
    viewport vp;
    this->getViewport(&vp);

    projection_matrix currentProjection = _projectionMatrixStack.top();

    std::shared_ptr<iscene_transformer> sceneTransformer = createSceneTransformer();
    sceneTransformer->setTransforms(
        _renderTransforms, 
        vp, 
        currentProjection.minDepth,
        currentProjection.maxDepth
        );

    return sceneTransformer;
}

// gets the current render target
inline std::shared_ptr<isurface> engine::getRenderTarget(const uint32 context)
{
    WRL::ComPtr<ID3D11RenderTargetView> renderTargetView;
    this->getContext(context)->OMGetRenderTargets(1, renderTargetView.GetAddressOf(), NULL);

    if (!renderTargetView.Get())
        return nullptr;

    return createSurfaceFromView(renderTargetView.Get(), texture_type::renderTarget);
}

// gets the current depth stencil target
inline std::shared_ptr<isurface> engine::getDepthStencilTarget(const uint32 context)
{
    WRL::ComPtr<ID3D11DepthStencilView> depthStencilView;
    this->getContext(context)->OMGetRenderTargets(1, NULL, depthStencilView.GetAddressOf());

    if (!depthStencilView.Get())
        return nullptr;

    return createSurfaceFromView(depthStencilView.Get(), texture_type::depthStencil);
}

// sets the current render target and depth buffer
inline void engine::setRenderTargets(
    const std::shared_ptr<isurface>& renderTarget,
    const std::shared_ptr<isurface>& depthStencilTarget,
    const uint32 context
    )
{
    win32_result wr;

    WRL::ComPtr<ID3D11RenderTargetView> renderTargetView;
    if (renderTarget)
    {
        std::shared_ptr<b::iunknown_provider> w = std::dynamic_pointer_cast<b::iunknown_provider>(renderTarget);
        WRL::ComPtr<IUnknown> punk = w->getInterface(d::texture_interfaces::render_target);  
        punk.As(&renderTargetView);
    }

    WRL::ComPtr<ID3D11DepthStencilView> depthStencilView;
    if (depthStencilTarget)
    {
        std::shared_ptr<b::iunknown_provider> w = std::dynamic_pointer_cast<b::iunknown_provider>(depthStencilTarget);
        WRL::ComPtr<IUnknown> punk = w->getInterface(d::texture_interfaces::depth_stencil);  
        punk.As(&depthStencilView);
    }

    ID3D11RenderTargetView* renderTargets[1] = { NULL };
    renderTargets[0] = renderTargetView.Get();
    this->getContext(context)->OMSetRenderTargets(1, renderTargets, depthStencilView.Get());
}

inline void engine::setPixelShader(const WRL::ComPtr<ID3D11PixelShader>& pixelShader, const uint32 context)
{
    if (_currentRenderMode == render_mode::normal)
    {
        this->getContext(context)->PSSetShader(pixelShader.Get(), NULL, 0);
    }
    else if (_currentRenderMode == render_mode::wireframe)
    {
        this->getContext(context)->PSSetShader(_wireframeShader.Get(), NULL, 0);
    }
    else
    {
        this->getContext(context)->PSSetShader(_overdrawShader.Get(), NULL, 0);
    }
}

// updates the texture resources for the specified effect
inline void engine::updateEffectTextures(const std::shared_ptr<ieffect2>& effect, const uint32 context)
{
    if (effect.get() != nullptr)
    {
        effect->setTexture(L"texture1", _textures[0], context);
        effect->setTexture(L"texture2", _textures[1], context);
        effect->setTexture(L"texture3", _textures[2], context);
        effect->setTexture(L"texture4", _textures[3], context);
        effect->setTexture(L"texture5", _textures[4], context);
        effect->setTexture(L"texture6", _textures[5], context);
        effect->setTexture(L"texture7", _textures[6], context);
        effect->setTexture(L"texture8", _textures[7], context);
    }
}

// updates material constant buffer
inline void engine::updateMaterialConstantBuffer()
{
    // get the material properties
    material_constants materialData;

    if (_material.get() != NULL)
    {
        materialData.ambient = _material->getAmbientColor();
        materialData.diffuse =_material->getDiffuseColor();
        materialData.specular = _material->getSpecularColor();
        materialData.emissive = _material->getEmissiveColor();
        materialData.specularPower = _material->getSpecularPower();
    }

    // copy data into constant buffer
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    win32_result((HRESULT)_d3d11DeviceContext->Map(_materialBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
    CopyMemory(mappedResource.pData, &materialData, sizeof(material_constants));
    _d3d11DeviceContext->Unmap(_materialBuffer.Get(), 0);

    _d3d11DeviceContext->VSSetConstantBuffers(
        0, // SLOT
        1,
        _materialBuffer.GetAddressOf()
        );
    _d3d11DeviceContext->PSSetConstantBuffers(
        0, // SLOT
        1,
        _materialBuffer.GetAddressOf()
        );
}

// updates the light constant buffer
inline void engine::updateLightConstantBuffer()
{
    light_constants lightData;
    uint32 activeLights = 0;
    uint32 i;

    // set ambient light
    lightData.ambient = _ambientLight;

    for (i = 0; i < g::constants::maxLights; i++)
    {
        if (_lights[i].enabled)
        {
            // flip light direction since shader code wants light direction to point towards the light,
            // but the "light direction" in light describes the direction the light is pointing.
            m::vector3d lightDirection = _lights[i].light.direction;
            lightDirection.normalize();
            lightDirection.scale(-1.0f);

            // setup light data
            lightData.lightColor[activeLights] = _lights[i].light.color;

            lightData.lightAttenuation[activeLights*4+0] = _lights[i].light.attenuation0;
            lightData.lightAttenuation[activeLights*4+1] = _lights[i].light.attenuation1;
            lightData.lightAttenuation[activeLights*4+2] = _lights[i].light.attenuation2;
            lightData.lightAttenuation[activeLights*4+3] = _lights[i].light.range;

            if (_lights[i].light.type == light_type::point)
            {
                lightData.lightDirection[activeLights*4+0] = _lights[i].light.position.x;
                lightData.lightDirection[activeLights*4+1] = _lights[i].light.position.y;
                lightData.lightDirection[activeLights*4+2] = _lights[i].light.position.z;
            }
            else
            {
                lightData.lightDirection[activeLights*4+0] = lightDirection.x;
                lightData.lightDirection[activeLights*4+1] = lightDirection.y;
                lightData.lightDirection[activeLights*4+2] = lightDirection.z;
            }
            
            lightData.lightSpecularIntensity[activeLights*4+0] = _lights[i].light.specularIntensity;

            lightData.isPointLight[activeLights*4+0] = (_lights[i].light.type == light_type::point ? 1 : 0);

            // increment our active light count
            activeLights++;
        }
    }

    // set active lights
    lightData.activeLights = activeLights;

    // copy data into constant buffer
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    win32_result((HRESULT)_d3d11DeviceContext->Map(_lightBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
    CopyMemory(mappedResource.pData, &lightData, sizeof(light_constants));
    _d3d11DeviceContext->Unmap(_lightBuffer.Get(), 0);

    _d3d11DeviceContext->VSSetConstantBuffers(
        1, // SLOT
        1,
        _lightBuffer.GetAddressOf()
        );
    _d3d11DeviceContext->PSSetConstantBuffers(
        1, // SLOT
        1,
        _lightBuffer.GetAddressOf()
        );
}

// updates the object constant buffer
inline void engine::updateObjectConstantBuffer()
{
    object_constants objectData;

    m::matrix44 m;

    // need to transpose localToWorld4x4
    _renderTransforms.localToWorldSpace.getTranspose(&objectData.localToWorld4x4);

    // need to transpose localToProjected4x4
    _renderTransforms.worldToProjectedSpace.applyFully(_renderTransforms.localToWorldSpace, &m);
    m.getTranspose(&objectData.localToProjected4x4);

    // need to transpose worldToLocal4x4
    _renderTransforms.worldToLocalSpace.getTranspose(&objectData.worldToLocal4x4);

    // need to transpose worldToView4x4
    _renderTransforms.worldToCameraSpace.getTranspose(&objectData.worldToView4x4);

    // need to transpose uvTransform4x4
    _uvMatrixStack.top().getTranspose(&objectData.uvTransform4x4);

    // copy eye position 
    _renderTransforms.cameraToWorldSpace.getTranslation(&objectData.eyePosition);

    // copy data into constant buffer
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    win32_result((HRESULT)_d3d11DeviceContext->Map(_objectBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
    CopyMemory(mappedResource.pData, &objectData, sizeof(object_constants));
    _d3d11DeviceContext->Unmap(_objectBuffer.Get(), 0);

    _d3d11DeviceContext->VSSetConstantBuffers(
        2, // SLOT
        1,
        _objectBuffer.GetAddressOf()
        );
    _d3d11DeviceContext->PSSetConstantBuffers(
        2, // SLOT
        1,
        _objectBuffer.GetAddressOf()
        );
}

// updates the misc constant buffer
inline void engine::updateMiscConstantBuffer(const uint32 context)
{
    viewport vp;
    this->getViewport(&vp, context);

    misc_constants miscData;
    miscData.viewportWidth = vp.width;
    miscData.viewportHeight = vp.height;
    miscData.time = _time;

    // copy data into constant buffer
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    win32_result((HRESULT)this->getContext(context)->Map(_miscBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource));
    CopyMemory(mappedResource.pData, &miscData, sizeof(misc_constants));
    this->getContext(context)->Unmap(_miscBuffer.Get(), 0);

    this->getContext(context)->VSSetConstantBuffers(
        3, // SLOT
        1,
        _miscBuffer.GetAddressOf()
        );
    this->getContext(context)->PSSetConstantBuffers(
        3, // SLOT
        1,
        _miscBuffer.GetAddressOf()
        );
}

// initializes members and optionally releases objects
inline void engine::initMembers(bool preserveState)
{
    _time = 0.0f;

    _immediateMode.isScreenSpace = false;
    _immediateMode.isActive = false;

    // if not preserving current render states, reset them to some known good values
    if (!preserveState)
    {
        m::matrix44 identMatrix;

        while (_worldMatrixStack.size() > 0)
        {
            _worldMatrixStack.pop();
        }
        _worldMatrixStack.push(identMatrix);

        while (_worldInverseMatrixStack.size() > 0)
        {
            _worldInverseMatrixStack.pop();
        }
        _worldInverseMatrixStack.push(identMatrix);

        while (_cameraMatrixStack.size() > 0)
        {
            _cameraMatrixStack.pop();
        }
        _cameraMatrixStack.push(identMatrix);

        while (_shadowStack.size() > 0)
        {
            _shadowStack.pop();
        }

        shadow_data sd;
        sd.matrix = identMatrix;
        sd.projection = projection_matrix();

        _shadowStack.push(sd);

        while (_projectionMatrixStack.size() > 0)
        {
            _projectionMatrixStack.pop();
        }
        _projectionMatrixStack.push(projection_matrix());

        while (_uvMatrixStack.size() > 0)
        {
            _uvMatrixStack.pop();
        }
        _uvMatrixStack.push(identMatrix);

        _renderTransforms.localToWorldSpace = identMatrix;
        _renderTransforms.worldToLocalSpace = identMatrix;
        _renderTransforms.worldToCameraSpace = identMatrix;
        _renderTransforms.cameraToWorldSpace = identMatrix;
        _renderTransforms.cameraToProjectedSpace = identMatrix;
        _renderTransforms.projectedToCameraSpace = identMatrix;
        _renderTransforms.worldToProjectedSpace = identMatrix;

        _ambientLight.set(1.0f, 0.0f, 0.0f, 0.0f);
    }
}

// forces the graphics device into a known state based upon current render states.
inline void engine::forceGraphicsIntoKnownState()
{
    BAJA_VERIFY(_d3d11Device != NULL);

    // reset immediate mode buffers
    _worldIMBuffer->reset();
    _screenIMBuffer->reset();
    _immediateMode.isScreenSpace = false;
    _immediateMode.isActive = false;

    // use a default material (this also causes material constant buffer to update)
    this->setMaterial(std::shared_ptr<imaterial>()); // set to null

    win32_result wr;

        // Get size from back buffer (in case display params height and width is zero, which means, full screen)
    DXGI_SWAP_CHAIN_DESC desc = {0};
    _dxgiSwapChain->GetDesc(&desc);

    // set viewport (this also causes viewport constant buffer to update)
    viewport vp;
    vp.position.x = 0.0f;
    vp.position.y = 0.0f;
    vp.width = (float32)desc.BufferDesc.Width;
    vp.height = (float32)desc.BufferDesc.Height;
    vp.minZ = 0.0f;
    vp.maxZ = 1.0f;
    this->setViewport(vp); // NOTE: this used to be cast to TRACEHR

    // set blend state to a known value
    D3D11_BLEND_DESC blendDesc = CD3D11_BLEND_DESC(CD3D11_DEFAULT());
    blendDesc.IndependentBlendEnable = TRUE;
    blendDesc.RenderTarget[0].BlendEnable = TRUE;
    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
    
    WRL::ComPtr<ID3D11BlendState> blendState;
    wr = _d3d11Device->CreateBlendState(&blendDesc, blendState.GetAddressOf()); // NOTE: this used to be cast to TRACEHR

    float32 blendFactor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
    uint32 sampleMask = 0xffffffff;

    _d3d11DeviceContext->OMSetBlendState(blendState.Get(), blendFactor, sampleMask);

    // update remaining constant buffers
    this->updateLightConstantBuffer();
    this->updateObjectConstantBuffer();
}

inline render_mode engine::getRenderMode()
{
    return _currentRenderMode;
}

inline void engine::setRenderMode(const render_mode renderMode)
{
    // set the new render mode only if it is different
    if (_currentRenderMode != renderMode)
    {
        // remember new render mode
        _currentRenderMode = renderMode;

        // get current blend state
        D3D11_BLEND_DESC blendDesc;
        float32 blendFactor[4];
        uint32 sampleMask;
        this->getCurrentBlendState(&blendDesc, blendFactor, &sampleMask);

        // get current depth stencil state
        D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
        uint32 stencilRef;
        this->getCurrentDepthStencilState(&depthStencilDesc, &stencilRef);

        // get current rasterizer state
        D3D11_RASTERIZER_DESC rasterizerDesc;
        this->getCurrentRasterizerState(&rasterizerDesc);

        // ensure some render states are set to known good values
        blendDesc.RenderTarget[0].BlendEnable = TRUE;
        blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
        blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
        blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
        blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
        blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
        blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
        rasterizerDesc.FillMode = D3D11_FILL_SOLID;
        rasterizerDesc.CullMode = D3D11_CULL_BACK;
        rasterizerDesc.FrontCounterClockwise = FALSE;

        // set the new states (which will further adjust render states based on mode)
        this->setBlendState(blendDesc, blendFactor, sampleMask);
        this->setDepthStencilState(depthStencilDesc, stencilRef);
        this->setRasterizerState(rasterizerDesc);
    }
}

inline void engine::getBackBufferSize(uint32* width, uint32* height)
{
    DXGI_SWAP_CHAIN_DESC desc = {0};
    _dxgiSwapChain->GetDesc(&desc);

    *width = desc.BufferDesc.Width;
    *height = desc.BufferDesc.Height;
}

// draw primitives to the render target
inline void engine::drawPrimitive(
    const primitive_type primitive,
    const uint32 startVertex,
    const uint32 primitiveCount,
    const uint32 context
    )
{
    this->getContext(context)->IASetPrimitiveTopology(d::toD3D11_PRIMITIVE_TOPOLOGY(primitive));

    uint32 vertexCount = 0;
    if (primitive == primitive_type::pointList)
    {
        vertexCount = primitiveCount;
    }
    else if (primitive == primitive_type::lineList)
    {
        vertexCount = primitiveCount * 2;
    }
    else if (primitive == primitive_type::lineStrip)
    {
        vertexCount = primitiveCount + 1;
    }
    else if (primitive == primitive_type::triangleList)
    {
        vertexCount = primitiveCount * 3;
    }
    else if (primitive == primitive_type::triangleStrip)
    {
        vertexCount = primitiveCount + 2;
    }

    this->getContext(context)->Draw(vertexCount, startVertex);
        
    _frameStats.primitivesDrawn[static_cast<int>(primitive)] += primitiveCount;
    _frameStats.totalPrimitives += primitiveCount;
    _frameStats.totalVertices += vertexCount;
}

// draw index primitives to the render target
inline void engine::drawIndexedPrimitive(
    const primitive_type primitive,
    const int32 baseVertexIndex,
    const uint32 minVertexIndex,
    const uint32 numVertices,
    const uint32 startIndex,
    const uint32 primitiveCount,
    const uint32 context
    )
{
    this->getContext(context)->IASetPrimitiveTopology(d::toD3D11_PRIMITIVE_TOPOLOGY(primitive));

    uint32 indexCount = 0;
    if (primitive == primitive_type::pointList)
    {
        indexCount = primitiveCount;
    }
    else if (primitive == primitive_type::lineList)
    {
        indexCount = primitiveCount * 2;
    }
    else if (primitive == primitive_type::lineStrip)
    {
        indexCount = primitiveCount + 1;
    }
    else if (primitive == primitive_type::triangleList)
    {
        indexCount = primitiveCount * 3;
    }
    else if (primitive == primitive_type::triangleStrip)
    {
        indexCount = primitiveCount + 2;
    }

    this->getContext(context)->DrawIndexed(indexCount, startIndex, baseVertexIndex);

    _frameStats.primitivesDrawn[static_cast<int32>(primitive)] += primitiveCount;
    _frameStats.totalPrimitives += primitiveCount;
    _frameStats.totalVertices += numVertices;
}

inline void engine::pushWorldMatrix(const m::matrix44& world, const bool combineWithCurrentWorld)
{
    m::matrix44 newWorld;
    m::matrix44 newWorldInverse;

    // we need the inverse of the world matrix
    m::matrix44 inverse;
    world.getInverse(&inverse);

    if (combineWithCurrentWorld == true)
    {
        BAJA_VERIFY(_worldMatrixStack.size() >= 1);
        BAJA_VERIFY(_worldInverseMatrixStack.size() >= 1);

        // apply world to current world matrix on the stack
        _worldMatrixStack.top().apply(world, &newWorld);

        // apply world inverse to current world inverse matrix on the stack
        inverse.apply(_worldInverseMatrixStack.top(), &newWorldInverse);
    }
    else
    {
        // we're not combining with current world matrix
        newWorld = world;
        newWorldInverse = inverse;
    }

    _worldMatrixStack.push(newWorld);
    _worldInverseMatrixStack.push(newWorldInverse);

    _renderTransforms.localToWorldSpace = newWorld;
    _renderTransforms.worldToLocalSpace = newWorldInverse;

    // update object constants
    this->updateObjectConstantBuffer();
}

inline void engine::popWorldMatrix()
{
    BAJA_VERIFY(_worldMatrixStack.size() != 0);

    // don't remove 1st matrix on the stack (which is always the identity matrix)
    if (_worldMatrixStack.size() > 1)
    {
        _worldMatrixStack.pop();
        _worldInverseMatrixStack.pop();
    }

    const m::matrix44& newWorld = _worldMatrixStack.top();
    const m::matrix44& newWorldInverse = _worldInverseMatrixStack.top();

    _renderTransforms.localToWorldSpace = newWorld;
    _renderTransforms.worldToLocalSpace = newWorldInverse;

    // update object constants
    this->updateObjectConstantBuffer();
}

inline void engine::pushCameraMatrix(const m::matrix44& camera)
{
    _cameraMatrixStack.push(camera);

    this->setCameraMatrix(camera);
}

inline void engine::replaceCurrentCameraMatrix(const m::matrix44& camera)
{
    size_t stackSize = _cameraMatrixStack.size();
    if (stackSize > 1)
    {
        // never pop off the identity matrix (always first matrix on the stack)
        _cameraMatrixStack.pop();
    }

    this->pushCameraMatrix(camera);
}

inline void engine::popCameraMatrix()
{
    size_t stackSize = _cameraMatrixStack.size();

    if (stackSize > 1)
    {
        _cameraMatrixStack.pop();
        stackSize--;
    }

    this->setCameraMatrix(_cameraMatrixStack.top());
}

inline void engine::pushOrthographicProjection(
    const float32 left, 
    const float32 right, 
    const float32 top, 
    const float32 bottom, 
    const float32 nearZ, 
    const float32 farZ
    )
{
    projection_matrix projection;

    // make the orthographic projection
    projection.matrix.makeOrthographicProjection(left, right, top, bottom, nearZ, farZ);

    // orthographic projections never have perspective divide (RHW) so
    // both min and max depth values are set to 1.0f
    projection.minDepth = 1.0f;
    projection.maxDepth = 1.0f;

    _projectionMatrixStack.push(projection);

    this->setProjectionMatrix(projection);
}

inline void engine::pushPerspectiveProjection(
    const float32 widthAtFocalPoint,
    const float32 heightAtFocalPoint, 
    const float32 nearZ,
    const float32 focalPointZ,
    const float32 farZ
    )
{
    projection_matrix projection;

    // make the perspective projection
    projection.matrix.makePerspectiveProjection(widthAtFocalPoint, heightAtFocalPoint, nearZ, focalPointZ, farZ);
    projection.minDepth = nearZ;
    projection.maxDepth = farZ;

    _projectionMatrixStack.push(projection);

    this->setProjectionMatrix(projection);
}

inline void engine::replaceCurrentProjectionWithOrthographicProjection(
    const float32 left, 
    const float32 right, 
    const float32 top, 
    const float32 bottom, 
    const float32 nearZ, 
    const float32 farZ
    )
{
    size_t stackSize = _projectionMatrixStack.size();
    if (stackSize < 2)
    {
        // stack size should never be 0, but could be 1 and we dont want to replace that projection (the identity)
        // so simply push this projection matrix instead
        this->pushOrthographicProjection(left, right, top, bottom, nearZ, farZ);
        return;
    }

    projection_matrix projection;

    // make the orthographic projection
    projection.matrix.makeOrthographicProjection(left, right, top, bottom, nearZ, farZ);

    // orthographic projections never have perspective divide (RHW) so
    // both min and max depth values are set to 1.0f
    projection.minDepth = 1.0f;
    projection.maxDepth = 1.0f;

    _projectionMatrixStack.pop();
    _projectionMatrixStack.push(projection);

    this->setProjectionMatrix(projection);
}

inline void engine::replaceCurrentProjectionWithPerspectiveProjection(
    const float32 widthAtFocalPoint,
    const float32 heightAtFocalPoint, 
    const float32 nearZ,
    const float32 focalPointZ,
    const float32 farZ
    )
{
    size_t stackSize = _projectionMatrixStack.size();
    if (stackSize < 2)
    {
        // stack size should never be 0, but could be 1 and we dont want to replace that projection (the identity)
        // so simply push this projection matrix instead
        this->pushPerspectiveProjection(widthAtFocalPoint, heightAtFocalPoint, nearZ, focalPointZ, farZ);
        return;
    }

    projection_matrix projection;

    // make the perspective projection
    projection.matrix.makePerspectiveProjection(widthAtFocalPoint, heightAtFocalPoint, nearZ, focalPointZ, farZ);
    projection.minDepth = nearZ;
    projection.maxDepth = farZ;

    _projectionMatrixStack.pop();
    _projectionMatrixStack.push(projection);

    this->setProjectionMatrix(projection);
}

inline void engine::popProjectionMatrix()
{
    if (_projectionMatrixStack.size() > 1)
    {
        _projectionMatrixStack.pop();
    }

    this->setProjectionMatrix(_projectionMatrixStack.top());
}

inline void engine::pushUVMatrix(const m::matrix44& uvMatrix)
{
    _uvMatrixStack.push(uvMatrix);
    
    this->updateObjectConstantBuffer();
}

inline void engine::popUVMatrix()
{
    BAJA_VERIFY(0 != _uvMatrixStack.size());

    // don't remove 1st matrix on the stack (which is always the identity matrix)
    if (_uvMatrixStack.size() > 1)
    {
        _uvMatrixStack.pop();
    }

    // update object constants
    this->updateObjectConstantBuffer();
}

inline m::matrix44 engine::getCoordinateSystemMatrix()
{
    return _coordinateSystemMatrix;
}

inline void engine::setCoordinateSystemMatrix(const m::matrix44& worldMatrix)
{
    _coordinateSystemMatrix = worldMatrix;
}

inline m::matrix44 engine::getWorldMatrix()
{
    return _renderTransforms.localToWorldSpace;
}

inline m::matrix44 engine::getWorldToProjectedMatrix()
{
    return _renderTransforms.worldToProjectedSpace;
}

inline m::matrix44 engine::getWorldToCameraMatrix()
{
    return _renderTransforms.worldToCameraSpace;
}

inline std::shared_ptr<ieffect2> engine::getStandardEffect(const standard_effect standardEffect)
{
    BAJA_CHECK_ARG(standardEffect < standard_effect::total);

    std::shared_ptr<ieffect2> returnValue =  _assetManager->getOrCreateEffect(
        shared_from_this(),
        _standardEffects[static_cast<uint32>(standardEffect)]
        );

    return returnValue;
}

inline void engine::begin(const primitive_type primitive)
{
    BAJA_CHECK_ARG(primitive <= primitive_type::triangleStrip);

    win32_result wr;
    if (_immediateMode.isActive) wr = E_FAIL; // ERROR:

    _immediateMode.isScreenSpace = false;

    _worldIMBuffer->begin(shared_from_this(), primitive);

    _immediateMode.isActive = true;
}

inline void engine::beginScreenSpace(const primitive_type primitive)
{
    BAJA_CHECK_ARG(primitive <= primitive_type::triangleStrip);

    win32_result wr;
    if (_immediateMode.isActive) wr = E_FAIL; // ERROR:

    _immediateMode.isScreenSpace = true;

    _screenIMBuffer->begin(shared_from_this(), primitive);

    _immediateMode.isActive = true;
}

inline void engine::setColor(const packed_color packedColor)
{
    _immediateMode.vertex.color = packedColor;
    _immediateMode.screenVertex.color = packedColor;
}

inline void engine::setNormal(const m::vector3d& normal)
{
    _immediateMode.vertex.normal = normal;
}

inline void engine::setTextureCoordinate(const texture_coordinate& texCoord)
{
    _immediateMode.vertex.uv[0] = texCoord;
    _immediateMode.screenVertex.uv[0] = texCoord;
}

inline void engine::setVertex(const m::point3d& position)
{
    _immediateMode.vertex.position = position;
    _worldIMBuffer->addVertices(shared_from_this(), &_immediateMode.vertex, sizeof(_immediateMode.vertex), 1);
}

inline void engine::setScreenSpaceVertex(const m::point4d& position)
{
    _immediateMode.screenVertex.position = position;
    return _screenIMBuffer->addVertices(shared_from_this(), &_immediateMode.screenVertex, sizeof(_immediateMode.screenVertex), 1);
}

inline void engine::end()
{
    win32_result wr;

    if (!_immediateMode.isActive) wr = E_FAIL;

    if (!_immediateMode.isScreenSpace)
    {
        _worldIMBuffer->end(shared_from_this());
    }
    else
    {
        _screenIMBuffer->end(shared_from_this());
    }

    _immediateMode.isActive = false;
    _immediateMode.isScreenSpace = false;
}

inline void engine::beginScene()
{
    // reset the frame stats
    _frameStats.reset();

    // if needed, reset peak stats
    if (_resetPeakStats)
    {
        _peakStats.reset();
        _resetPeakStats = false;
    }

    if (_currentRenderMode != _nextRenderMode)
    {
        this->setRenderMode(_nextRenderMode);
    }
}

inline void engine::endScene()
{
    _peakStats.maximize(_frameStats);
}

inline void engine::showScene()
{
    // The application may optionally specify "dirty" or "scroll"
    // rects to improve efficiency in certain scenarios.
    DXGI_PRESENT_PARAMETERS parameters = {0};
    parameters.DirtyRectsCount = 0;
    parameters.pDirtyRects = nullptr;
    parameters.pScrollRect = nullptr;
    parameters.pScrollOffset = nullptr;
    
    // The first argument instructs DXGI to block until VSync, putting the application
    // to sleep until the next VSync. This ensures we don't waste any cycles rendering
    // frames that will never be displayed to the screen.
    HRESULT hr = _dxgiSwapChain->Present1(1, 0, &parameters);
}

inline void engine::executeDeferred()
{
#if 0
    WRL::ComPtr<ID3D11Debug> debug;

    _d3d11Device.As(&debug);
    debug->ReportLiveDeviceObjects(D3D11_RLDO_FLAGS::D3D11_RLDO_DETAIL);
#endif

    WRL::ComPtr<ID3D11CommandList> cl;
    _d3d11DeviceContextDeferred->FinishCommandList(true, cl.GetAddressOf());
    _d3d11DeviceContext->ExecuteCommandList(cl.Get(), true);

#if 0
    _d3d11Device.As(&debug);
    debug->ReportLiveDeviceObjects(D3D11_RLDO_FLAGS::D3D11_RLDO_DETAIL);
#endif
}

inline std::shared_ptr<ivar_group> engine::getVarGroup()
{
    return _graphicsVarGroup;
}

// private methods

inline WRL::ComPtr<ID3D11DeviceContext1> engine::getContext(const uint32 context)
{
    return (context == 0) ? _d3d11DeviceContext : _d3d11DeviceContextDeferred;
}

inline void engine::addStandardEffectDescription(
    const standard_effect e,
    const wchar* name,
    const wchar* pixelShaderFile,
    const wchar* vertexShaderFile
    )
{
    std::shared_ptr<effect2_description> desc;
    
    desc = std::make_shared<effect2_description>(name, pixelShaderFile, vertexShaderFile);
    desc->textureDesc.push_back(texture_description(L"texture1", 0));
    desc->textureDesc.push_back(texture_description(L"texture2", 1));
    desc->textureDesc.push_back(texture_description(L"texture3", 2));
    desc->textureDesc.push_back(texture_description(L"texture4", 3));
    desc->textureDesc.push_back(texture_description(L"texture5", 4));
    desc->textureDesc.push_back(texture_description(L"texture6", 5));
    desc->textureDesc.push_back(texture_description(L"texture7", 6));
    desc->textureDesc.push_back(texture_description(L"texture8", 7));

    std::vector<const memory> returnValue;
    returnValue.resize(sizeof(D3D11_BLEND_DESC));

    D3D11_BLEND_DESC* bd =  reinterpret_cast<D3D11_BLEND_DESC*>(returnValue.data());

    std::memset(bd, 0, sizeof(D3D11_BLEND_DESC));
    bd->AlphaToCoverageEnable = false;
    bd->IndependentBlendEnable = false;
    bd->RenderTarget[0].BlendEnable = false;
    bd->RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
    bd->RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
    bd->RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    bd->RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

    desc->blendStateDesc.push_back(state_description(L"blend_solid", returnValue));

    returnValue.resize(sizeof(D3D11_BLEND_DESC));

    bd =  reinterpret_cast<D3D11_BLEND_DESC*>(returnValue.data());

    std::memset(bd, 0, sizeof(D3D11_BLEND_DESC));
    bd->AlphaToCoverageEnable = false;
    bd->IndependentBlendEnable = false;
    bd->RenderTarget[0].BlendEnable = true;
    bd->RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
    bd->RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    bd->RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    bd->RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
    bd->RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
    bd->RenderTarget[0].BlendOpAlpha= D3D11_BLEND_OP_ADD;
    bd->RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

    desc->blendStateDesc.push_back(state_description(L"blend_alpha", returnValue));

    // HLSL equivalent:
    //SamplerState textureSampler
    //{
    //    Filter = MIN_MAG_MIP_LINEAR;
    //    AddressU = Clamp;
    //    AddressV = Clamp;
    //};

    returnValue.resize(sizeof(D3D11_SAMPLER_DESC));

    D3D11_SAMPLER_DESC* d = reinterpret_cast<D3D11_SAMPLER_DESC*>(returnValue.data());

    std::memset(d, 0, sizeof(D3D11_SAMPLER_DESC));

    d->Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    d->AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
    d->AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    d->AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
    d->MinLOD = -FLT_MAX;
    d->MaxLOD = FLT_MAX;
    d->MaxAnisotropy = 16;
    d->ComparisonFunc = D3D11_COMPARISON_NEVER;

    desc->samplerStateDesc.push_back(g::state_description(L"linear_sampler", returnValue));

    _standardEffects[static_cast<uint32>(e)] = desc;
}

inline void engine::initialize(const std::shared_ptr<iservice_map>& services)
{
    // setup the render targets
    this->setRenderTargetViews();
    this->setD2DRenderTarget();

    _assetManager = createAssetManager();

    // create the NULL texture
    this->createNullTexture();

    // allocate light array
    _lights.resize(g::constants::maxLights);
    for (uint32 i = 0; i < g::constants::maxLights; i++)
    {
        _lights[i].reset();
    }

    win32_result wr;

    // create material constant buffer
    D3D11_BUFFER_DESC desc;
    ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
    desc.ByteWidth = sizeof(material_constants);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0;
    desc.StructureByteStride = 0;

    wr = _d3d11Device->CreateBuffer(&desc, NULL, _materialBuffer.GetAddressOf()); 

    // create lights constant buffer
    ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
    desc.ByteWidth = sizeof(light_constants);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0;
    desc.StructureByteStride = 0;

    wr = _d3d11Device->CreateBuffer(&desc, NULL, _lightBuffer.GetAddressOf()); 

    // create object constant buffer
    ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
    desc.ByteWidth = sizeof(object_constants);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0;
    desc.StructureByteStride = 0;

    wr = _d3d11Device->CreateBuffer(&desc, NULL, _objectBuffer.GetAddressOf()); 

    // create misc constant buffer
    ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
    desc.ByteWidth = sizeof(misc_constants);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0;
    desc.StructureByteStride = 0;

    wr = _d3d11Device->CreateBuffer(&desc, NULL, _miscBuffer.GetAddressOf()); 

    // create the standard effect for our standard effect pipeline
    this->addStandardEffectDescription(	
        standard_effect::colored,
        L"standard_effect_colored",
        L"standard_effect_colored_pixel_shader.cso",
        L"standard_effect_colored_vertex_shader.cso"
        );

    this->addStandardEffectDescription(	
        standard_effect::textured,
        L"standard_effect_textured",
        L"standard_effect_textured_pixel_shader.cso",
        L"standard_effect_textured_vertex_shader.cso"
        );

    this->addStandardEffectDescription(	
        standard_effect::coloredTextured,
        L"standard_effect_colored_textured",
        L"standard_effect_colored_textured_pixel_shader.cso",
        L"standard_effect_colored_textured_vertex_shader.cso"
        );

    this->addStandardEffectDescription(	
        standard_effect::litColored,
        L"standard_effect_lit_colored",
        L"standard_effect_lit_colored_pixel_shader.cso",
        L"standard_effect_lit_colored_vertex_shader.cso"
        );

    this->addStandardEffectDescription(	
        standard_effect::litTextured,
        L"standard_effect_lit_textured",
        L"standard_effect_lit_textured_pixel_shader.cso",
        L"standard_effect_lit_textured_vertex_shader.cso"
        );

    this->addStandardEffectDescription(		
        standard_effect::litColoredTextured,
        L"standard_effect_lit_colored_textured",
        L"standard_effect_lit_colored_textured_pixel_shader.cso",
        L"standard_effect_lit_colored_textured_vertex_shader.cso"
        );

    this->addStandardEffectDescription(	
        standard_effect::screenColored,
        L"standard_effect_screen_colored",
        L"standard_effect_colored_pixel_shader.cso",
        L"standard_effect_screen_space_vertex_shader.cso"
        );

    this->addStandardEffectDescription(		
        standard_effect::screenTextured,
        L"standard_effect_screen_textured",
        L"standard_effect_textured_pixel_shader.cso",
        L"standard_effect_screen_space_vertex_shader.cso"
        );

    this->addStandardEffectDescription(		
        standard_effect::screenColoredTextured,
        L"standard_effect_screen_colored_textured",
        L"standard_effect_colored_textured_pixel_shader.cso",
        L"standard_effect_screen_space_vertex_shader.cso"
        );

    // add the mesh effect/techniques to the standard effects
    this->addStandardEffectDescription(		
        standard_effect::meshUnlit,
        L"unlit",
        L"unlit_pixel_shader.cso",
        L"unlit_vertex_shader.cso"
        );

    this->addStandardEffectDescription(		
        standard_effect::meshLambert,
        L"lambert",
        L"lambert_pixel_shader.cso",
        L"lit_vertex_shader.cso"
        );

    this->addStandardEffectDescription(	
        standard_effect::meshPhong,
        L"phong",
        L"phong_pixel_shader.cso",
        L"lit_vertex_shader.cso"
        );

    // create vertex decl for lit colored textured verts
    std::shared_ptr<ivertex_declaration> litColoredTexturedVertexDecl;
    litColoredTexturedVertexDecl = this->createVertexDeclaration();

    litColoredTexturedVertexDecl->beginDefinition(5);
    litColoredTexturedVertexDecl->addElement(0, 0, vertex_decl_type::float3, vertex_decl_usage::position, 0);
    litColoredTexturedVertexDecl->addElement(0, 12, vertex_decl_type::float3, vertex_decl_usage::normal, 0);
    litColoredTexturedVertexDecl->addElement(0, 24, vertex_decl_type::float4, vertex_decl_usage::tangent, 0);
    litColoredTexturedVertexDecl->addElement(0, 40, vertex_decl_type::packedColor, vertex_decl_usage::color, 0);
    litColoredTexturedVertexDecl->addElement(0, 44, vertex_decl_type::float2, vertex_decl_usage::texCoord, 0);

    std::shared_ptr<ieffect2> e = this->getStandardEffect(standard_effect::litColoredTextured);
    litColoredTexturedVertexDecl->endDefinition(e->vertexShaderBytes(), e->vertexShaderSize()); 

    _assetManager->addVertexDeclaration(g::globals::standardVertexDeclName_LitColoredTextured, litColoredTexturedVertexDecl);

    // add mesh vertex declaration (same as lit colored textured)
    _assetManager->addVertexDeclaration(g::globals::standardVertexDeclName_Mesh, litColoredTexturedVertexDecl);

    // create vertex decl for screen colored textured verts
    std::shared_ptr<ivertex_declaration> screenColoredTexturedVertexDecl;
    screenColoredTexturedVertexDecl = this->createVertexDeclaration();
    screenColoredTexturedVertexDecl->beginDefinition(3);
    screenColoredTexturedVertexDecl->addElement(0, 0, vertex_decl_type::float4, vertex_decl_usage::transformedPosition, 0);
    screenColoredTexturedVertexDecl->addElement(0, 16, vertex_decl_type::packedColor, vertex_decl_usage::color, 0);
    screenColoredTexturedVertexDecl->addElement(0, 20, vertex_decl_type::float2, vertex_decl_usage::texCoord, 0);

    e = this->getStandardEffect(standard_effect::screenColoredTextured);
    screenColoredTexturedVertexDecl->endDefinition(e->vertexShaderBytes(), e->vertexShaderSize()); 

    _assetManager->addVertexDeclaration(g::globals::standardVertexDeclName_ScreenColoredTextured, screenColoredTexturedVertexDecl);

    // create the world immediate mode buffer
    _worldIMBuffer = createDynamicVertexBuffer(
        this->shared_from_this(),
        litColoredTexturedVertexDecl,
        sizeof(vertex_lit_colored_textured),
        constants::immediateModeVerticesPerBuffer
        );

    // create the screen immediate mode buffer
    _screenIMBuffer = createDynamicVertexBuffer(
        this->shared_from_this(),
        screenColoredTexturedVertexDecl,
        sizeof(vertex_screen_colored_textured),
        constants::immediateModeVerticesPerBuffer
        );

    // create overdraw shader
    std::shared_ptr<imemory_stream> ms = createMemoryStreamFromFile(L"overdraw_pixel_shader.cso");
    wr = _d3d11Device->CreatePixelShader(
            ms->getBytes().data(),
            ms->getBytes().size(),
            nullptr,
            _overdrawShader.GetAddressOf()
            );

    // create wireframe shader
    ms = createMemoryStreamFromFile(L"wireframe_pixel_shader.cso");
    wr = _d3d11Device->CreatePixelShader(
            ms->getBytes().data(),
            ms->getBytes().size(),
            nullptr,
            _wireframeShader.GetAddressOf()
            );

    // create a d2d state block
    wr = _d2d1Factory->CreateDrawingStateBlock(&_d2d1StateBlock);

    // initialize device and place into a known state
    this->initMembers(false);
    this->forceGraphicsIntoKnownState();

    // setup the var wrappers
    _graphicsVarGroup = createVarGroup(_engineName.c_str());
    int enumValues[] = 
    { 
        static_cast<int>(render_mode::normal),
        static_cast<int>(render_mode::wireframe),
        static_cast<int>(render_mode::overdraw)
    };

    wchar* enumNames[] =
    {
        L"Normal",
        L"Wireframe",
        L"Overdraw"
    };

    // add name var
    _graphicsVarGroup->addStringVar(
        L"Name",
        const_cast<wchar*>(_engineName.c_str()),
        0);

    // add var to enable different render modes
    _graphicsVarGroup->addEnumVar(
        L"Render Mode",
        (int*)&_nextRenderMode,
        enumValues,
        (const wchar**)enumNames,
        3,
        0);

    // add var to reset the peak stats
    _graphicsVarGroup->addBoolVar(
        L"Reset Peak Stats",
        &_resetPeakStats,
        0);
    // add vars for current frame stats
    _graphicsVarGroup->addUintVar(
        L"Total Point List Primitives",
        &_frameStats.primitivesDrawn[static_cast<int32>(primitive_type::pointList)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Total Line List Primitives",
        &_frameStats.primitivesDrawn[static_cast<int32>(primitive_type::lineList)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Total Line Strip Primitives",
        &_frameStats.primitivesDrawn[static_cast<int32>(primitive_type::lineStrip)],
        0,
        0,
        0,
        varFlagReadOnly);
    _graphicsVarGroup->addUintVar(
        L"Total Triangle List Primitives",
        &_frameStats.primitivesDrawn[static_cast<int32>(primitive_type::triangleStrip)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Total Triangle Strip Primitives",
        &_frameStats.primitivesDrawn[static_cast<int32>(primitive_type::triangleStrip)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Total Primitives",
        &_frameStats.totalPrimitives,
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Total Vertices",
        &_frameStats.totalVertices,
        0,
        0,
        0,
        varFlagReadOnly);

    // add vars for peak stats
    _graphicsVarGroup->addUintVar(
        L"Peak Point List Primitives",
        &_peakStats.primitivesDrawn[static_cast<int32>(primitive_type::pointList)],
        0,
        0,
        0,
        varFlagReadOnly);
    
    _graphicsVarGroup->addUintVar(
        L"Peak Line List Primitives",
        &_peakStats.primitivesDrawn[static_cast<int32>(primitive_type::lineList)],
        0,
        0,
        0,
        varFlagReadOnly);
    
    _graphicsVarGroup->addUintVar(
        L"Peak Line Strip Primitives",
        &_peakStats.primitivesDrawn[static_cast<int32>(primitive_type::lineStrip)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Peak Triangle List Primitives",
        &_peakStats.primitivesDrawn[static_cast<int32>(primitive_type::triangleList)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Peak Triangle Strip Primitives",
        &_peakStats.primitivesDrawn[static_cast<int32>(primitive_type::triangleStrip)],
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Peak Primitives",
        &_peakStats.totalPrimitives,
        0,
        0,
        0,
        varFlagReadOnly);

    _graphicsVarGroup->addUintVar(
        L"Peak Vertices",
        &_peakStats.totalVertices,
        0,
        0,
        0,
        varFlagReadOnly);
}

// sets camera matrix
inline void engine::setCameraMatrix(const m::matrix44& camera)
{
    // compute inverse of camera is needed to create the view matrix
    m::matrix44 viewMatrix;
    camera.getInverse(&viewMatrix);

    _renderTransforms.cameraToWorldSpace = camera;
    _renderTransforms.worldToCameraSpace = viewMatrix;

    // update the world to projected space matrix
    _renderTransforms.cameraToProjectedSpace.applyFully(
        _renderTransforms.worldToCameraSpace,
        &_renderTransforms.worldToProjectedSpace);

    // update object constants
    this->updateObjectConstantBuffer();
}

// sets the projection matrix
inline void engine::setProjectionMatrix(const projection_matrix& projection)
{
    m::matrix44 finalProjection;
    projection.matrix.applyFully(_coordinateSystemMatrix, &finalProjection);

    _renderTransforms.cameraToProjectedSpace = finalProjection;
    finalProjection.getInverse(&_renderTransforms.projectedToCameraSpace);

    // update the world to projected space matrix
    _renderTransforms.cameraToProjectedSpace.applyFully(
        _renderTransforms.worldToCameraSpace,
        &_renderTransforms.worldToProjectedSpace);

    // update object constants
    this->updateObjectConstantBuffer();
}

// sets up the render target views
inline void engine::setRenderTargetViews(const uint32 context)
{
    win32_result wr;

    // create render target view
    WRL::ComPtr<ID3D11Texture2D> backBuffer;
    wr = _dxgiSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);

    WRL::ComPtr<ID3D11RenderTargetView> renderTargetView;
    wr = _d3d11Device->CreateRenderTargetView(backBuffer.Get(), nullptr, &renderTargetView);

    // create depth-stencil resource
    WRL::ComPtr<ID3D11Texture2D> depthStencil;

    uint32 width = 0;
    uint32 height = 0;
    this->getBackBufferSize(&width, &height);

    D3D11_TEXTURE2D_DESC depthDesc;
    ZeroMemory(&depthDesc, sizeof(depthDesc));
    depthDesc.Width = width;
    depthDesc.Height = height;
    depthDesc.MipLevels = 1;
    depthDesc.ArraySize = 1;
    depthDesc.Format = d::toDXGI_FORMAT(_displayParams.depthStencilFormat);
    depthDesc.SampleDesc.Count = _msaaCount;
    depthDesc.SampleDesc.Quality = _msaaQualityLevel;
    depthDesc.Usage = D3D11_USAGE_DEFAULT;
    depthDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    depthDesc.CPUAccessFlags = 0;
    depthDesc.MiscFlags = 0;

    wr = _d3d11Device->CreateTexture2D(&depthDesc, NULL, depthStencil.GetAddressOf());

    // create depth stencil view
    WRL::ComPtr<ID3D11DepthStencilView> depthStencilView;
    wr = _d3d11Device->CreateDepthStencilView(depthStencil.Get(), NULL, depthStencilView.GetAddressOf());

    // set render targets
    ID3D11RenderTargetView* renderTargetViewArray[1];
    renderTargetViewArray[0] = renderTargetView.Get();
    this->getContext(context)->OMSetRenderTargets(1, renderTargetViewArray, depthStencilView.Get());
}

// NOTE: the way this works is we set a fixed D2D render target that corresponds to the entire backbuffer surface
// this is currently only assumed by the font code
// the better way to do this would be to manage D2D surfaces just like the D3D ones.  i.e. we'd have a setRenderTargets method
// for D2D and allow the font code to create its own D2D surface and set it
// of course, note that even for 2D targets the app needs to know to re-create them and re-set them when there's a resize
// by default we'll create some default render targets (see setrenderTargetViews) that can be overriden by the app
// also note we don't store a pointer to these anywhere besides passing them to the context, this is so when we resize we don't 
// get any 0x887a0001 from resizeBuffers (DXGI ERROR: Swapchain cannot be resized unless all outstanding buffer references have been released.)
// because we won't have any outstanding referneces to these buffers.
inline void engine::setD2DRenderTarget()
{
    win32_result wr;

    // set D2D render target (back buffer surface)
    // this is used for the font rendering

    // get the backbuffer and create a bitmap render target from it
    WRL::ComPtr<ID3D11Texture2D> texture;
    wr = _dxgiSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(texture.GetAddressOf()));

    float32 dpix = 0;
    float32 dpiy = 0;
    _d2d1DeviceContext->GetDpi(&dpix, &dpiy);

    WRL::ComPtr<IDXGISurface> surface;
    wr = texture.As(&surface);

    D2D1_BITMAP_PROPERTIES1 bitmapProperties = 
        D2D1::BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            D2D1::PixelFormat(d::toDXGI_FORMAT(pixel_format::d32BitColorNoAlpha), D2D1_ALPHA_MODE_PREMULTIPLIED),
            dpix,
            dpiy
            );

    WRL::ComPtr<ID2D1Bitmap1> backBufferRenderTarget;
    wr = _d2d1DeviceContext->CreateBitmapFromDxgiSurface(
            surface.Get(),
            &bitmapProperties,
            backBufferRenderTarget.GetAddressOf()
            );

    _d2d1DeviceContext->SetTarget(backBufferRenderTarget.Get());
}

// creates the texture used when NULL is specified as a texture.
// texture is a simple 1x1 32bit all white texture.
// this allows for proper blending with material colors, etc in
// pixel shaders (otherwise, triangles end up black)
inline void engine::createNullTexture()
{
    packed_color white = packColor(0xff, 0xff, 0xff, 0xff);
    packed_color* data = nullptr;
    uint32 pitch = 0;

    BAJA_VERIFY(_nullTexture.get() == NULL);

    // create texture used when texture is NULL
    _nullTexture = this->createTexture(
        1,
        1,
        1,
        0,
        pixel_format::d32BitColorWithAlpha,
        memory_pool::default);
 
    _nullTexture->lock(
        lock_type::write,
        (memory**)&data,
        &pitch
        );

    *data = white;

    _nullTexture->unlock();
}

}}} // namespace baja.graphics.dx11_1

#include <baja/graphics/dx11.1/effect2_impl.hpp>
#include <baja/graphics/dx11.1/texture_impl.hpp>
#include <baja/graphics/dx11.1/surface_impl.hpp>
#include <baja/graphics/dx11.1/vertex_buffer_impl.hpp>
#include <baja/graphics/dx11.1/dynamic_vertex_buffer_impl.hpp>
#include <baja/graphics/dx11.1/vertex_declaration_impl.hpp>
#include <baja/graphics/dx11.1/index_buffer_impl.hpp>
#include <baja/graphics/dx11.1/material_impl.hpp>
#include <baja/graphics/dx11.1/material_collection_impl.hpp>
#include <baja/graphics/dx11.1/mesh_impl.hpp>
#include <baja/graphics/dx11.1/asset_manager_impl.hpp>
#include <baja/graphics/dx11.1/scene_transformer_impl.hpp>
#include <baja/graphics/dx11.1/font_impl.hpp>

namespace baja { namespace graphics {

template <class Tcontext>
inline std::shared_ptr<iengine> 
createEngine(
    const std::shared_ptr<iservice_map>& services,
    const bool useWARP,
    const display_params& dp,
    const Tcontext& context
    )
{
    return baja::graphics::dx11_1::engine::create(services, useWARP, dp, context);
}

inline void checkIsValid(const bool useWARP)
{
    // when WARP is used we are always valid
    if (useWARP) return; 

    win32_result wr;

    // create the device
    WRL::ComPtr<ID3D11Device> d3d11Device;
    WRL::ComPtr<ID3D11DeviceContext> d3d11DeviceContext;
    D3D_FEATURE_LEVEL supportedFeatureLevels[] = 
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };

    D3D_FEATURE_LEVEL d3d11FeatureLevel = D3D_FEATURE_LEVEL_10_0;
    wr = D3D11CreateDevice(
        NULL,
        useWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_HARDWARE,
        NULL,
        D3D11_CREATE_DEVICE_SINGLETHREADED,
        supportedFeatureLevels,
        sizeof(supportedFeatureLevels)/sizeof(D3D_FEATURE_LEVEL),
        D3D11_SDK_VERSION,
        &d3d11Device,
        &d3d11FeatureLevel,
        &d3d11DeviceContext
        );
}

}} // namespace baja.graphics
